home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacHack 1997
/
MacHack 1997.toast
/
Hacks
/
Hacks ’96
/
Talking Telnet
/
source
/
Url
/
url.c
next >
Wrap
Text File
|
1996-06-22
|
18KB
|
666 lines
/****************************************************************
* NCSA Telnet for the Macintosh *
* *
* National Center for Supercomputing Applications *
* Software Development Group *
* 152 Computing Applications Building *
* 605 E. Springfield Ave. *
* Champaign, IL 61820 *
* *
* Copyright (c) 1986-1994, *
* Board of Trustees of the University of Illinois *
****************************************************************/
#include "rsinterf.proto.h"
#include "IConfig.proto.h"
#include "url.proto.h"
#include "vsdata.h"//because the damn prototype on the next line needs it
#include "vsinterf.proto.h"
TURLKind ParseURL(Handle urlH);
Boolean MyStrNEqual (char *s1, char *s2, short n);
OSErr FindAppOnVolume (OSType sig, short vRefNum, FSSpec *file);
OSErr FindAppFromSig (OSType sig, FSSpec *fSpec, Boolean *running,
ProcessSerialNumber *psn);
OSErr FindRunningAppBySignature (OSType sig, FSSpec *fSpec,
ProcessSerialNumber *psn);
OSErr LaunchAppWithEventAndString (Boolean running, FSSpec *appSpec, ProcessSerialNumber *psn,
OSType eventClass, OSType eventID, OSType keyword, char *str,
unsigned short launchFileFlags, unsigned short launchControlFlags);
OSErr OpenHelperWithURL (OSType sig, char *url);
OSErr GetLastModDateTime(FSSpec *fSpec, unsigned long *lastModDateTime);
OSErr VolHasDesktopDB (short vRefNum, Boolean *hasDesktop);
OSErr GetIndVolume (short index, short *vRefNum);
#define kGetURLEventClass 'GURL'
#define kGetURLEventID 'GURL'
static unsigned char gTable[256] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
0x41, 0x41, 0x43, 0x45, 0x4E, 0x4F, 0x55, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x43, 0x45, 0x45,
0x45, 0x45, 0x49, 0x49, 0x49, 0x49, 0x4E, 0x4F, 0x4F, 0x4F, 0x4F, 0x4F, 0x55, 0x55, 0x55, 0x55,
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0x4F,
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xAE, 0x4F,
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0x22, 0x22, 0xC9, 0x20, 0x41, 0x41, 0x4F, 0xAE, 0xAE,
0xD0, 0xD1, 0x22, 0x22, 0x27, 0x27, 0xD6, 0xD7, 0x59, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
};
char *gURLSchemeNames[] = {
":", /* kNotURL */
"mailto:",
"news:",
"nntp:",
"ftp:",
"http:",
"gopher:",
"wais:",
"telnet:",
"rlogin:",
"tn3270:",
"finger:",
"whois:",
nil
};
void UnloadURL(void) { }
Boolean MyStrNEqual (char *s1, char *s2, short n)
{
unsigned char *t = gTable;
unsigned char *us1 = (unsigned char *)s1;
unsigned char *us2 = (unsigned char *)s2;
while (--n >= 0 && t[*us1++] == t[*us2++]) /* do nothing */;
return n < 0;
}
#define CR 0x0d /* the carriage return character */
#define isLWSP(a) ((a) == ' ' || (a) == '\t')
#define isLWSPorCR(a) (isLWSP(a) || (a) == CR)
void HandleURL(short w)
{
Handle urlH;
TURLKind urlKind;
OSErr err;
OSType sig;
urlH = RSGetTextSel(w, 0);
if ((urlH == (char **)-1L) || (urlH == nil)) {
return;
}
HLock(urlH);
urlKind = ParseURL(urlH);
if (urlKind == kNotURL)
return;
else if ((urlKind == kTelnetURL)||(urlKind == kRloginURL))
{ //we handle this, send apple event to ourselves
ProcessSerialNumber psn;
unsigned short launchControlFlags;
FlashSelection(w);
launchControlFlags = launchContinue | launchNoFileFlags;
launchControlFlags |= launchDontSwitch;
GetCurrentProcess(&psn);
err = LaunchAppWithEventAndString(TRUE, NULL, &psn,
kGetURLEventClass, kGetURLEventID,
keyDirectObject, *urlH, 0, launchControlFlags);
return;
}
else
sig = GetHelperInfo(urlKind);
if (sig == NULL)
return;
FlashSelection(w);
err = OpenHelperWithURL(sig, *urlH);
return;
}
TURLKind ParseURL(Handle urlH)
{
short size;
char *textBegin, *textEnd, *p;
TURLKind urlKind;
char *schemeName;
size = GetHandleSize(urlH);
textBegin = *urlH;
textEnd = *urlH + size - 1;
/* strip off leading white space and lagging white space */
while (isLWSPorCR(*textBegin) && textBegin < textEnd) textBegin++;
while (isLWSPorCR(*textBegin) && textBegin < textEnd) textEnd--;
/* look for < and > on each end, strip em off */
if (*textBegin == '<')
textBegin++;
if (*textEnd == '>')
textEnd--;
/* look for URL:, strip it*/
if ((MyStrNEqual("URL:", textBegin, 4)))
textBegin += 4;
size = textEnd - textBegin + 1;
if (size <= 0)
return kNotURL;
/* strip CR inside */
p = textBegin;
while (p < textEnd)
{
if (*p == CR)
{
BlockMoveData(p+1, p,size - (p-textBegin + 1));
size--;
textEnd--;
}
else
p++;
}
/* Clean up and NULL terminate */
BlockMoveData(textBegin, *urlH, size);
*(*urlH + size) = 0;
/* get url type */
p = *urlH;
while ((*p != ':')&&(p < *urlH + size - 1)) p++;
if (p - *urlH >= size - 1) // colon not found
{
/* might be mail address */
//if (isMailAddress(urlH))
// return kMailtoURL;
// else
return kNotURL;
}
for (urlKind = kNotURL; ; urlKind++) {
schemeName = gURLSchemeNames[urlKind];
if (schemeName == nil) return kNotURL;
if (MyStrNEqual(*urlH, schemeName, strlen(schemeName))) return urlKind;
}
}
OSErr FindAppOnVolume (OSType sig, short vRefNum, FSSpec *file)
{
DTPBRec pb;
OSErr err = noErr;
short ioDTRefNum, i;
FInfo fInfo;
FSSpec candidate;
unsigned long lastModDateTime, maxLastModDateTime;
memset(&pb, 0, sizeof(DTPBRec));
pb.ioCompletion = nil;
pb.ioVRefNum = vRefNum;
pb.ioNamePtr = nil;
err = PBDTGetPath(&pb);
if (err != noErr) return err;
ioDTRefNum = pb.ioDTRefNum;
memset(&pb, 0, sizeof(DTPBRec));
pb.ioCompletion = nil;
pb.ioIndex = 0;
pb.ioFileCreator = sig;
pb.ioNamePtr = file->name;
pb.ioDTRefNum = ioDTRefNum;
err = PBDTGetAPPLSync(&pb);
if (err == fnfErr || err == paramErr) return afpItemNotFound;
if (err != noErr) return err;
file->vRefNum = vRefNum;
file->parID = pb.ioAPPLParID;
err = FSpGetFInfo(file, &fInfo);
if (err == noErr) return noErr;
i = 1;
maxLastModDateTime = 0;
while (true) {
memset(&pb, 0, sizeof(DTPBRec));
pb.ioCompletion = nil;
pb.ioIndex = i;
pb.ioFileCreator = sig;
pb.ioNamePtr = candidate.name;
pb.ioDTRefNum = ioDTRefNum;
err = PBDTGetAPPLSync(&pb);
if (err != noErr) break;
candidate.vRefNum = vRefNum;
candidate.parID = pb.ioAPPLParID;
err = GetLastModDateTime(file, &lastModDateTime);
if (err == noErr) {
if (lastModDateTime > maxLastModDateTime) {
maxLastModDateTime = lastModDateTime;
*file = candidate;
}
}
i++;
}
return maxLastModDateTime > 0 ? noErr : afpItemNotFound;
}
/*----------------------------------------------------------------------------
GetLastModDateTime
Get the last mod date and time of a file.
Entry: fSpec = pointer to file spec.
Exit: function result = error code.
*lastModDateTime = last mod date and time.
----------------------------------------------------------------------------*/
OSErr GetLastModDateTime(FSSpec *fSpec, unsigned long *lastModDateTime)
{
CInfoPBRec pBlock;
OSErr err = noErr;
pBlock.hFileInfo.ioNamePtr = fSpec->name;
pBlock.hFileInfo.ioVRefNum = fSpec->vRefNum;
pBlock.hFileInfo.ioFDirIndex = 0;
pBlock.hFileInfo.ioDirID = fSpec->parID;
err = PBGetCatInfoSync(&pBlock);
if (err != noErr) return err;
*lastModDateTime = pBlock.hFileInfo.ioFlMdDat;
return noErr;
}
/*----------------------------------------------------------------------------
VolHasDesktopDB
Check to see if a volume supports the new desktop database.
Entry: vRefNum = vol ref num of volumn
Exit: function result = error code.
*hasDesktop = true if volume has the new desktop database.
----------------------------------------------------------------------------*/
OSErr VolHasDesktopDB (short vRefNum, Boolean *hasDesktop)
{
HParamBlockRec pb;
GetVolParmsInfoBuffer info;
OSErr err = noErr;
pb.ioParam.ioCompletion = nil;
pb.ioParam.ioNamePtr = nil;
pb.ioParam.ioVRefNum = vRefNum;
pb.ioParam.ioBuffer = (Ptr)&info;
pb.ioParam.ioReqCount = sizeof(info);
err = PBHGetVolParmsSync(&pb);
*hasDesktop = err == noErr && (info.vMAttrib & (1L << bHasDesktopMgr)) != 0;
return err;
}
OSErr FindAppFromSig (OSType sig, FSSpec *fSpec, Boolean *running,
ProcessSerialNumber *psn)
{
OSErr err = noErr;
short sysVRefNum, vRefNum, index;
Boolean hasDesktopDB;
long junk;
if (running != nil) {
err = FindRunningAppBySignature(sig, fSpec, psn);
*running = true;
if (err == noErr) return noErr;
*running = false;
if (err != procNotFound) return err;
}
err = FindFolder(kOnSystemDisk, kSystemFolderType, false, &sysVRefNum, &junk);
if (err != noErr) return err;
vRefNum = sysVRefNum;
index = 0;
while (true) {
if (index == 0 || vRefNum != sysVRefNum) {
err = VolHasDesktopDB(vRefNum, &hasDesktopDB);
if (err != noErr) return err;
if (hasDesktopDB) {
err = FindAppOnVolume(sig, vRefNum, fSpec);
if (err != afpItemNotFound) return err;
}
}
index++;
err = GetIndVolume(index, &vRefNum);
if (err == nsvErr) return fnfErr;
if (err != noErr) return err;
}
}
OSErr GetIndVolume (short index, short *vRefNum)
{
ParamBlockRec pb;
OSErr err = noErr;
pb.volumeParam.ioCompletion = nil;
pb.volumeParam.ioNamePtr = nil;
pb.volumeParam.ioVolIndex = index;
err = PBGetVInfoSync(&pb);
*vRefNum = pb.volumeParam.ioVRefNum;
return err;
}
/*----------------------------------------------------------------------------
FindRunningAppBySignature
Find a running app given its signature.
Entry: sig = signature of app.
Exit: function result = error code.
= procNotFound if not running.
*fSpec = file spec of app.
*psn = process serial number of running app.
----------------------------------------------------------------------------*/
OSErr FindRunningAppBySignature (OSType sig, FSSpec *fSpec,
ProcessSerialNumber *psn)
{
OSErr err = noErr;
ProcessInfoRec info;
psn->highLongOfPSN = 0;
psn->lowLongOfPSN = kNoProcess;
while (true) {
err = GetNextProcess(psn);
if (err != noErr) return err;
info.processInfoLength = sizeof(info);
info.processName = nil;
info.processAppSpec = fSpec;
err = GetProcessInformation(psn, &info);
if (err != noErr) return err;
if (info.processSignature == sig) return noErr;
}
}
/*----------------------------------------------------------------------------
LaunchAppWithEventAndString
Launch an application with an initial event with a string parameter.
Entry: running = true if application is running, in which case
it is sent the odoc event.
appSpec = file spec of application.
*psn = process serial number of app if it is running.
eventClass = event class.
eventID = event id.
keyword = parameter keyword (keyDirectObject if string is the
direct object).
str = the string parameter for the event.
launchFileFlags = file flags.
launchControlFlags = control flags.
Exit: function result = error code.
----------------------------------------------------------------------------*/
OSErr LaunchAppWithEventAndString (Boolean running, FSSpec *appSpec, ProcessSerialNumber *psn,
OSType eventClass, OSType eventID, OSType keyword, char *str,
unsigned short launchFileFlags, unsigned short launchControlFlags)
{
ProcessSerialNumber thePSN;
LaunchParamBlockRec launchThis;
AEDesc target = {0, nil};
AEDesc stringDesc = {0, nil};
AEDesc launchDesc = {0, nil};
AppleEvent theEvent = {0, nil};
AppleEvent theReply = {0, nil};
OSErr err = noErr;
Boolean autoParamValue = false;
if (running) thePSN = *psn;
err = AECreateDesc(typeProcessSerialNumber, &thePSN, sizeof(thePSN), &target);
if (err != noErr) goto exit;
err = AECreateAppleEvent(eventClass, eventID, &target,
kAutoGenerateReturnID, kAnyTransactionID, &theEvent);
if (err != noErr) goto exit;
err = AECreateDesc(typeChar, str, strlen(str), &stringDesc);
if (err != noErr) goto exit;
err = AEPutParamDesc(&theEvent, keyword, &stringDesc);
if (err != noErr) goto exit;
if (running) {
err = AESend(&theEvent, &theReply, kAENoReply, kAENormalPriority, kNoTimeOut,
nil, nil);
if (err != noErr) goto exit;
if ((launchControlFlags & launchDontSwitch) == 0) {
err = SetFrontProcess(psn);
if (err != noErr) goto exit;
}
} else {
err = AECoerceDesc(&theEvent, typeAppParameters, &launchDesc);
if (err != noErr) goto exit;
HLock(theEvent.dataHandle);
launchThis.launchAppSpec = appSpec;
launchThis.launchAppParameters = (AppParametersPtr)*launchDesc.dataHandle;
launchThis.launchBlockID = extendedBlock;
launchThis.launchEPBLength = extendedBlockLen;
launchThis.launchFileFlags = launchFileFlags;
launchThis.launchControlFlags = launchControlFlags;
err = LaunchApplication(&launchThis);
}
exit:
if (target.dataHandle != nil) AEDisposeDesc(&target);
if (stringDesc.dataHandle != nil) AEDisposeDesc(&stringDesc);
if (launchDesc.dataHandle != nil) AEDisposeDesc(&launchDesc);
if (theEvent.dataHandle != nil) AEDisposeDesc(&theEvent);
if (theReply.dataHandle != nil) AEDisposeDesc(&theReply);
return err;
}
/*----------------------------------------------------------------------------
OpenHelperWithURL
Open a helper program and pass it a URL string.
Entry: helperInfo = pointer to helper info.
url = URL string.
Exit: function result = error code.
----------------------------------------------------------------------------*/
OSErr OpenHelperWithURL (OSType sig, char *url)
{
OSErr err = noErr;
ProcessSerialNumber psn;
FSSpec appSpec;
Boolean running;
unsigned short launchControlFlags;
launchControlFlags = launchContinue | launchNoFileFlags;
err = FindAppFromSig(sig, &appSpec, &running, &psn);
if (err != noErr) return err;
err = LaunchAppWithEventAndString(running, &appSpec, &psn,
kGetURLEventClass, kGetURLEventID,
keyDirectObject, url, 0, launchControlFlags);
return err;
}
#define isurlschemechar(c) (isalnum((c)) || c == '+' || c == '.' || c == '-')
Boolean FindURLAroundPoint(Point curr, short w)
{
/* called by RSSelect when Command Click has occured outside any selection.
This routine looks for urls around the current point, makes that the selected area if
it finds one, and returns TRUE. Otherwise leaves the current selection area untouched
and returns FALSE. */
/* Note: the point we receive is already normalized to a character position */
short columns;
Handle block;
char *original, *start, *end, *textEnd, *p, *q;
short i, numLines = 1;
char EOLS = CR;
short neededLines, endLine = 0, startLine = 0;
short blockSize;
Point startPoint, endPoint;
short currentLine;
char *tempBlockPtr;
short tempCounter, lineLength[20];
Boolean found = FALSE, allowSpaces=FALSE;
columns = VSgetcols(w) + 1; //VSgetcols returns one less than the number of columns
/* we need enough space to find a 255 char URL on either side of the cursor */
neededLines = 510/columns;
if (255%columns != 0)
neededLines += 2;
/* want it to be symmetric about this line */
if (neededLines%2 == 0)
neededLines += 1;
blockSize = neededLines*(columns + 1); //we need space for the \r
block = myNewHandle((long)blockSize);
textEnd = *block + blockSize;
HLock(block);
/* get the lines we need */
VSgettext(w, 0, curr.v-(neededLines-1)/2, columns-1, curr.v+(neededLines-1)/2, *block, blockSize, &EOLS, 0);
original = *block;
for (i = 0; i < (neededLines-1)/2; i++)
{
while (*original != CR)
original++;
original++;
}
original+= curr.h + 1;
tempBlockPtr = *block;
for (i = 0; i < neededLines; i++)
{
tempCounter = 0;
while (*tempBlockPtr++ != CR) tempCounter++;
lineLength[i] = tempCounter;
}
p = original;
q = original - 1;
currentLine = (neededLines-1)/2 + 1;
while ((!found)&&(p > *block))
{
while (p >= *block && !isLWSPorCR(*p) && *p != '<' && *p != '"') p--;
if (*p == CR)
{
if (lineLength[currentLine - 2] == columns)
{
p--; //this is just a wrapped line
currentLine--;
}
else
found = TRUE;
}
else
found = TRUE;
}
startLine = currentLine;
if (*p != '<')
p++;
else
allowSpaces = TRUE;
currentLine = (neededLines-1)/2 + 1;
found = FALSE;
while (!found)
{
while (q < textEnd && (*q != CR) &&(!isLWSP(*q) || allowSpaces)
&& *q != '>' && *q != '"') q++;
if (*q == CR)
{
if (lineLength[currentLine-1] == columns)
{
q++; //this is just a wrapped line
currentLine++;
}
else
found = TRUE;
}
else
found = TRUE;
}
endLine = currentLine;
if (*q != '>') q--;
if (p >= q) return FALSE;
while (p < q && (isLWSPorCR(*p))) p++;
while (p < q && (isLWSPorCR(*q))) q--;
if (*p == '<')
{
if (*q != '>') return FALSE;
}
else
{
if (*q == '>') return FALSE;
}
start = p;
end = q;
UnHiliteSelection(w);
HUnlock(block);
startPoint.v = curr.v - ((neededLines-1)/2 + 1 - startLine);
endPoint.v = curr.v + (endLine -(neededLines-1)/2 - 1);
if (startPoint.v == curr.v)
startPoint.h = curr.h - (original - start);
else
{
long numChars = 0;
for (i = 0; i < startLine - 1; i++)
numChars += lineLength[i] + 1;
startPoint.h = (start - *block - numChars - 1);
}
if (endPoint.v == curr.v)
endPoint.h = curr.h + (end - original + 1);
else
{
long numChars = 0;
for (i = 0; i < endLine - 1; i++)
numChars += lineLength[i] + 1;
endPoint.h = (end - *block - numChars);
}
HiliteThis(w, startPoint, endPoint);
return TRUE;
}